namespace Ex
{
    using System.Collections;

    ///<summary>
    ///从lua端产生，传回来的值，不可从C#端修改
    ///</summary>
    public class ExLuaTable : ExTable, IExLuaElement
    {
        private ExLuaTable(EnvLua env, IDictionary? table = null) : base(env, table)
        {
        }


        ///<summary>
        ///可以获得实际值
        ///</summary>
        public static ExLuaTable WatchFromLuaStack(EnvLua env, int table_index)
        {

            lock (env)
            {
                var lua = env.Current;
                var lua_table = new ExLuaTable(env);
                object key = UsableLuaType.nil;
                table_index = table_index < 0 ? table_index - 1 : table_index;
                while (true)
                {
                    env.PushObjectToLuaStack(key);
                    if (!lua.Next(table_index))
                    {
                        break;
                    }
                    var value = env.FetchObjectFromLuaStack();
                    key = env.FetchObjectFromLuaStack() ?? UsableLuaType.nil;
                    if (value == null || key is UsableLuaType.nil)
                    {
                        throw new Exception("Invalid type");
                    }
                    lua_table.table[key] = value;
                }
                return lua_table;
            }
        }

        public static ExLuaTable FetchFromLuaStack(EnvLua env)
        {
            lock (env)
            {
                var result = WatchFromLuaStack(env, -1);
                env.Current.Pop(1);
                return result;
            }
        }

        /// <summary>
        /// 绕开env的取值策略，只取值类型。
        /// </summary>
        /// <param name="env"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        /// <exception cref="InvalidDataException"></exception>
        public static object FetchOnlyValueFromLuaStack(EnvLua env, UsableLuaType type = UsableLuaType.any)
        {

            lock (env)
            {
                if (type == UsableLuaType.any)
                {
                    type = Tool.GetTypeOnStack(env.Current, -1);
                }
                object? result = null;
                switch (type)
                {
                    case UsableLuaType.nil:
                        result = UsableLuaType.nil;
                        break;
                    case UsableLuaType.boolean:
                        result = env.Current.ToBoolean(-1);
                        env.Current.Pop(1);
                        break;
                    case UsableLuaType.number:
                        result = env.Current.CheckNumber(-1);
                        env.Current.Pop(1);
                        break;
                    case UsableLuaType.luaString:
                        result = env.Current.CheckString(-1);
                        env.Current.Pop(1);
                        break;
                    case UsableLuaType.cfunction:
                        result = ExCsFunctionRef.FetchFromLuaStack(env);
                        break;
                    case UsableLuaType.function:
                        result = ExLuaFunction.FetchFromLuaStack(env);
                        break;
                    case UsableLuaType.table:
                        var lua = env.Current;
                        var lua_table = new ExLuaTable(env);
                        object key = UsableLuaType.nil;
                        while (true)
                        {
                            env.PushObjectToLuaStack(key);
                            if (!lua.Next(-2))
                            {
                                break;
                            }
                            var value = FetchOnlyValueFromLuaStack(env);
                            key = FetchOnlyValueFromLuaStack(env) ?? UsableLuaType.nil;
                            if (value == null || key is UsableLuaType.nil)
                            {
                                throw new Exception("Invalid type");
                            }
                            lua_table.table[key] = value;
                        }
                        result = lua_table;
                        env.Current.Pop(1);
                        break;
                    case UsableLuaType.csobject:
                        result = ExCsRawData.FetchFromLuaStack(env);
                        break;
                    default:
                        throw new InvalidDataException("Invalid type:" + type);
                }
                return result;
            }
        }


    }
}